package com.qd.panda.mq.charge;

import cn.hutool.core.math.Money;
import com.cdqidi.exception.ApiException;
import com.qd.common.panda.domain.entity.api.UnpaidAmountDTO;
import com.qd.common.panda.domain.entity.stroke.UserStrokeDTO;
import com.qd.common.panda.domain.entity.stroke.UserStrokeOrderDTO;
import com.qd.panda.dc.DcService;
import com.qd.panda.dc.dto.PayInfoDTO;
import com.qd.panda.dc.dto.charge.ChargeActionReqDTO;
import com.qd.panda.dc.dto.charge.StatusChargeRespDTO;
import com.qd.panda.model.UserPlateRelation;
import com.qd.panda.model.UserStroke;
import com.qd.panda.service.plate.UserPlateRelationService;
import com.qd.panda.service.stroke.BigDecimalUtil;
import com.qd.panda.service.stroke.DisplayUtil;
import com.qd.panda.service.stroke.UserStrokeService;
import com.qd.panda.service.stroke.order.UserStrokeOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author sjk
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ExecutorService {

    private final UserPlateRelationService userPlateRelationService;
    private final DcService dcService;
    private final UserStrokeService userStrokeService;
    private final UserStrokeOrderService userStrokeOrderService;
    private final ChargeConsumerService chargeConsumerService;
    private final EtcPayService etcPayService;
    @Autowired
    @Qualifier("strokeThreadPoolExecutor")
    private ThreadPoolExecutor threadPoolExecutor;

    /**
     * 提供给数据中台查询当前车辆未交的停车费和充电费
     * 在查询到有费用的时候更新订单
     *
     * @param parkId      停车场ID
     * @param plateNumber 车牌号
     * @return 合计费用
     */
    @Transactional(rollbackFor = Exception.class)
    public UnpaidAmountDTO getUnpaidAmount(String parkId, String plateNumber) {
        if(log.isDebugEnabled()) {
            log.debug("数据中台来查询停车费和充电费,parkId: {},plateNumber: {}", parkId, plateNumber);
        }
        UnpaidAmountDTO unpaidAmountDTO = new UnpaidAmountDTO();
        String userId = Optional.ofNullable(userPlateRelationService.getUniqueByPlateNumber(plateNumber)).map(UserPlateRelation::getUserId).orElse(null);
        UserStrokeDTO userStrokeDTO;
        if (null == userId) {
            log.error("根据车牌号查询不到用户信息,plateNumber: {}", plateNumber);
            return unpaidAmountDTO;
        } else {
            UserStrokeDTO query = new UserStrokeDTO();
            query.setUserId(userId);
            query.setParkId(parkId);
            final UserStroke userStroke = userStrokeService.getProcessStroke(query);
            query.freeData();
            if (null == userStroke) {
                log.error("获得行程为空,不处理,plateNumber: {},parkId: {}", plateNumber, parkId);
                return unpaidAmountDTO;
            } else {
                userStrokeDTO = userStrokeService.modelToDto(userStroke);
                if(log.isDebugEnabled()) {
                    log.debug("行程信息: {}", userStrokeDTO);
                }
            }
        }
        List<UserStrokeOrderDTO> payList = new ArrayList<>();
        buildPayList(userStrokeDTO, payList, unpaidAmountDTO);

        Money allMoney = new Money(unpaidAmountDTO.getChargeFee()).add(new Money(unpaidAmountDTO.getParkFee()));
        unpaidAmountDTO.setAllFee(BigDecimalUtil.buildDefaultScale(allMoney.getAmount()).stripTrailingZeros());
        if (userStrokeDTO.getPayAmount().longValue() > 0) {
            //说明产生了费用，需要支付
            etcPayService.etcChargeParkPay(userStrokeDTO.getSid(), payList);
        } else {
            return unpaidAmountDTO;
        }
        //判断是否支付成功，支付成功的话更新订单和行程信息
        boolean isPaySuccess = etcPayService.update(userStrokeDTO,payList);
        if (isPaySuccess) {
            //支付成功了就说明没有欠费信息了，返回0就行
            return new UnpaidAmountDTO();
        }
        userStrokeDTO.freeData();
        return unpaidAmountDTO;
    }


    private void buildPayList(UserStrokeDTO userStrokeDTO, List<UserStrokeOrderDTO> payList, UnpaidAmountDTO unpaidAmountDTO) {
        final CompletableFuture<String> chargeCompletableFuture = CompletableFuture.supplyAsync(() -> {
            UserStrokeOrderDTO orderQuery = new UserStrokeOrderDTO();
            orderQuery.setSid(userStrokeDTO.getSid());
            orderQuery.setTripType(DisplayUtil.TRIP_TYPE_1);
            final List<UserStrokeOrderDTO> chargeList = userStrokeOrderService.getListDto(orderQuery);
            orderQuery.freeData();
            if (!chargeList.isEmpty()) {
                Money allCharge = new Money();
                for (UserStrokeOrderDTO userStrokeOrderDTO : chargeList) {
                    if (userStrokeOrderDTO.getOrderCharge().getState() == 4) {
                        //表示已经结束
                        if (!userStrokeOrderDTO.getPayStatus().equals(DisplayUtil.PAY_STATUS_1)) {
                            allCharge = allCharge.add(new Money(userStrokeOrderDTO.getPayAmount()));
                            payList.add(userStrokeOrderDTO);
                        }
                    } else {
                        final ChargeActionReqDTO chargeActionReqDTO = new ChargeActionReqDTO()
                                .setAction(ChargeActionReqDTO.ACTION_STATUS)
                                .setStartChargeSeq(userStrokeOrderDTO.getThirdPartyOrderId())
                                .setConnectorID(userStrokeOrderDTO.getOrderCharge().getConnectorID())
                                .setOperatorID(userStrokeOrderDTO.getOrderCharge().getOperatorID());
                        StatusChargeRespDTO statusCharge = dcService.statusCharge(userStrokeOrderDTO.getParkId(), chargeActionReqDTO);
                        chargeActionReqDTO.freeData();
                        if (null != statusCharge) {
                            if(log.isDebugEnabled()) {
                                log.debug("充电费用,chargeOrder: {}", statusCharge.getTotalMoney());
                            }
                            allCharge.add(new Money(statusCharge.getTotalMoney()));
                            //更新充电订单信息
                            buildUserStrokeOrderCharge(userStrokeOrderDTO, statusCharge);
                            payList.add(userStrokeOrderDTO);
                        }
                    }

                }
                if (null != unpaidAmountDTO && allCharge.getCent() > 0) {
                    unpaidAmountDTO.setChargeFee(BigDecimalUtil.buildDefaultScale(allCharge.getAmount()).stripTrailingZeros());
                }
            }
            return null;
        }, threadPoolExecutor);
        final CompletableFuture<PayInfoDTO> parkCompletableFuture = chargeConsumerService.getParkOrder(userStrokeDTO.getParkId(),userStrokeDTO.getPlateNumber(),userStrokeDTO.getSid());

        CompletableFuture.allOf(chargeCompletableFuture, parkCompletableFuture).whenComplete((a, b) -> {
            try {
                Optional.ofNullable(parkCompletableFuture.get()).ifPresent(payInfoDTO -> {
                    if(log.isDebugEnabled()) {
                        log.debug("停车费用: {}", payInfoDTO.getLastFee());
                    }
                    if (null != unpaidAmountDTO) {
                        final Money money = new Money(payInfoDTO.getLastFee());
                        unpaidAmountDTO.setParkFee(BigDecimalUtil.buildDefaultScale(money.getAmount()));
                    }
                    //保存或者更新停车费用
                    payList.add(createUserStrokeOrderPark(payInfoDTO, userStrokeDTO));
                });
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new ApiException(e);
            } catch (ExecutionException e) {
                throw new ApiException(e);
            }
        });
    }


    private void buildUserStrokeOrderCharge(UserStrokeOrderDTO userStrokeOrderDTO, StatusChargeRespDTO statusCharge) {
        if (null == userStrokeOrderDTO) {
            return;
        }
        if (null == statusCharge) {
            return;
        }
        chargeConsumerService.buildUserStrokeOrderCharge(statusCharge, userStrokeOrderDTO);

    }

    private UserStrokeOrderDTO createUserStrokeOrderPark(PayInfoDTO payInfoDTO, UserStrokeDTO userStrokeDTO) {
        if (null == userStrokeDTO) {
            return null;
        }
        if (null == payInfoDTO) {
            return null;
        }
        return chargeConsumerService.createUserStrokeOrderPark(payInfoDTO, userStrokeDTO);
    }
}

